/* Add class members to type information */

#ifndef MC_ADD_MEMBER_HPP
#define MC_ADD_MEMBER_HPP

#include <cassert>
#include <cstddef>
#include <typeinfo>

/* TR1 header */
#ifdef __GLIBCXX__
	#include <tr1/type_traits>
#else
	#include <type_traits>
#endif

#include <memchart/accessor_read.hpp>
#include <memchart/accessor_read_pointer.hpp>
#include <memchart/accessor_write.hpp>
#include <memchart/demangle.hpp>
#include <memchart/exception.hpp>
#include <memchart/platform.hpp>
#include <memchart/rt_class_info.hpp>
#include <memchart/type_traits.hpp>
#include <memchart/types.hpp>

namespace memchart
{
	/* Default implementation */
	template <bool ShowMembers, typename C, typename T, T C::* M>
	struct add_member_impl
	{
		class member_info_impl : public rt_member_info
		{
			public:

				member_info_impl (const string & name)
					: rt_member_info(name, demangle(typeid(T).name()))
				{ }

				string read (const void *p) const
				{
					return accessor_read<C, T, M>::read(p);
				}

				bool is_writable () const
				{
					return accessor_write<C, T, M>::is_writable();
				}

				void write (void *p, const string & val) const
				{
					accessor_write<C, T, M>::write(p, val);
				}

				bool is_pointer () const
				{
					typedef typename std::tr1::remove_cv<
						typename std::tr1::remove_pointer<T>::type
						>::type base_type;

					return is_object_pointer<T>::value &&
						(std::tr1::is_same<C, base_type>::value ||
							show_members<base_type>::value);
				}

				uintptr_t read_pointer (const void *p) const
				{
					return accessor_read_pointer<C, T, M>::read(p);
				}
		};

		static void add (rt_member_vector & arr, const string & name)
		{
			static const member_info_impl member(name);

			arr.push_back(&member);
		}
	};


	/* Nested class implementation */
	template <typename C, typename T, T C::* M>
	struct add_member_impl<true, C, T, M>
	{
		class member_info_impl : public rt_member_info
		{
			public:

				member_info_impl (const string & container_name,
					const rt_member_info *inner)
					: rt_member_info(container_name + "." + inner->name,
						inner->type),
					inner(inner)
				{ }

				string read (const void *p) const
				{
					assert(p != NULL);

					const C & obj = *reinterpret_cast<const C *>(p);

					return inner->read(&(obj.*M));
				}

				bool is_writable () const
				{
					return !is_cv_qualified<T>::value && inner->is_writable();
				}

				void write (void *p, const string & val) const
				{
					if (is_cv_qualified<T>::value)
						throw conversion_error();
					else
					{
						assert(p != NULL);

						C & obj = *reinterpret_cast<C *>(p);

						inner->write(&(obj.*M), val);
					}
				}

				bool is_pointer () const
				{
					return inner->is_pointer();
				}

				uintptr_t read_pointer (const void *p) const
				{
					assert(p != NULL);

					const C & obj = *reinterpret_cast<const C *>(p);

					return inner->read_pointer(&(obj.*M));
				}

			private:

				const rt_member_info *inner;
		};

		static void add (rt_member_vector & arr, const string & name)
		{
			static vector<member_info_impl> members;

			const rt_class_info & inner = T::_memchart_class_info();

			members.reserve(inner.count());

			/* Convert and add all nested class members */
			for (size_t i = 0; i < inner.count(); ++i)
			{
				members.push_back(member_info_impl(name, &inner[i]));
				arr.push_back(&*members.rbegin());
			}
		}
	};


	template <typename T, T M>
	struct add_member
	{ };

	template <typename C, typename T, T C::* M>
	struct add_member<T C::*, M> :
		add_member_impl<
				show_members<typename std::tr1::remove_cv<T>::type>::value,
				C, T, M
			>
	{ };

	#define MC_ADD_MEMBER(arr, name, m) \
		memchart::add_member<MC_TYPEOF(m), m>::add(arr, name)
}

#endif /* MC_ADD_MEMBER_HPP */
